package cloudeval.spec

import cn.edu.neu.vmmigeval.evaluation.Measure

/**
 * The evaluation specification.
 *
 * @author yfwz100
 */
class EvaluationSpec {

    /**
     * The evaluation symbol.
     *
     * @author yfwz100
     */
    static abstract class Symbol implements Comparable<Symbol> {

        /**
         * The sum symbol, internal use only.
         */
        private static class SumSymbol extends Symbol {

            private Symbol a, b

            SumSymbol(Symbol a, Symbol b) {
                this.a = a
                this.b = b
            }

            @Override
            double getValue() {
                return a.value + b.value
            }
        }

        /**
         * The minus symbol, internal use only.
         */
        private static class MinusSymbol extends Symbol {

            private Symbol a, b

            MinusSymbol(Symbol a, Symbol b) {
                this.a = a
                this.b = b
            }

            @Override
            double getValue() {
                return a.value - b.value
            }
        }

        /**
         * The multiply symbol, internal use only.
         */
        private static class MultiplySymbol extends Symbol {

            private Symbol a, b

            MultiplySymbol(Symbol a, Symbol b) {
                this.a = a
                this.b = b
            }

            @Override
            double getValue() {
                return a.value * b.value
            }
        }

        /**
         * Division symbol, internal use only.
         */
        private static class DivideSymbol extends Symbol {

            private Symbol a, b

            DivideSymbol(Symbol a, Symbol b) {
                this.a = a
                this.b = b
            }

            @Override
            double getValue() {
                return a.value / b.value
            }
        }

        /**
         * Mod symbol, internal use only.
         */
        private static class ModSymbol extends Symbol {

            private Symbol a, b

            ModSymbol(Symbol a, Symbol b) {
                this.a = a
                this.b = b
            }

            @Override
            double getValue() {
                return a.value % b.value
            }
        }

        /**
         * Negative symbol, internal use only.
         */
        private static class NegativeSymbol extends Symbol {

            private Symbol symbol;

            NegativeSymbol(Symbol symbol) {
                this.symbol = symbol
            }

            @Override
            double getValue() {
                return - symbol.value
            }
        }

        // Operator override

        Symbol plus(Symbol a) {
            new SumSymbol(this, a);
        }

        Symbol minus(Symbol a) {
            new MinusSymbol(this, a);
        }

        Symbol multiply(Symbol a) {
            new MultiplySymbol(this, a)
        }

        Symbol div(Symbol a) {
            new DivideSymbol(this, a)
        }

        Symbol mod(Symbol a) {
            new ModSymbol(this, a)
        }

        Symbol negative() {
            new NegativeSymbol(this)
        }

        Symbol positive() {
            this
        }

        @Override
        int compareTo(Symbol o) {
            return this.value <=> o.value
        }

        /**
         * Get the double value of the current symbol.
         *
         * @return the double value.
         */
        abstract double getValue()
    }

    /**
     * The numeric symbol.
     */
    public static class NumberSymbol extends Symbol {
        double value

        NumberSymbol(double val) {
            value = val
        }
    }

    /**
     * The closure symbol.
     */
    public static class ClosureSymbol extends Symbol {

        private Closure<Symbol> closure

        ClosureSymbol(Closure<Symbol> closure) {
            this.closure = closure
        }

        @Override
        double getValue() {
            closure().getValue()
        }
    }

    /**
     * The measure symbol.
     */
    public static class MeasureSymbol extends Symbol {
        Measure measure

        MeasureSymbol(Measure m) {
            measure = m
        }

        @Override
        double getValue() {
            return measure.value
        }
    }

    private Map<String, Measure> measures = new HashMap<>()
    private Map<String, Symbol> metrics = new HashMap<>()

    /**
     * Monitor the given measure.
     *
     * @param a the measure.
     * @return the symbol.
     */
    Symbol monitor(Measure a) {
        measures[a.name] = a
        new MeasureSymbol(a)
    }

    /**
     * Monitor the given class of measure.
     *
     * @param a the measure.
     * @return the symbol.
     */
    Symbol monitor(Class<Measure> a) {
        monitor a.newInstance()
    }

    /**
     * Add the given metric.
     *
     * @param closure the metric.
     * @return the symbol.
     */
    public Symbol metric(String name, Closure<Symbol> closure) {
        def code = closure.rehydrate(this, this, this)
        code.resolveStrategy = Closure.DELEGATE_FIRST
        def m = new ClosureSymbol(code)
        metrics[name] = m
        m
    }

    Map<String, Measure> getMeasures() { measures }

    Map<String, Symbol> getMetrics() { metrics }
}
